<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>JavaKeeper</title>
    <meta name="generator" content="VuePress 1.5.4">
    <link rel="icon" href="/icon.svg">
    <script>
        var _hmt = _hmt || [];
        (function() {
            var hm = document.createElement("script");
            hm.src = "https://hm.baidu.com/hm.js?a949a9b30eb86ac0159e735ff8670c03";
            var s = document.getElementsByTagName("script")[0];
            s.parentNode.insertBefore(hm, s);
            // 引入谷歌,不需要可删除这段
            var hm1 = document.createElement("script");
            hm1.src = "https://www.googletagmanager.com/gtag/js?id=UA-169923503-1";
            var s1 = document.getElementsByTagName("script")[0]; 
            s1.parentNode.insertBefore(hm1, s1);
        })();
        // 谷歌加载,不需要可删除
        window.dataLayer = window.dataLayer || [];
        function gtag(){dataLayer.push(arguments);}
        gtag('js', new Date());
        gtag('config', 'UA-169923503-1');
    </script>
    <meta name="description" content="">
    <meta name="keywords" content="JavaKeeper,Java,Java开发,算法,blog">
    <link rel="preload" href="/assets/css/0.styles.91f57736.css" as="style"><link rel="preload" href="/assets/js/app.447d4224.js" as="script"><link rel="preload" href="/assets/js/3.9d76740c.js" as="script"><link rel="preload" href="/assets/js/1.c4fd7d2e.js" as="script"><link rel="preload" href="/assets/js/173.96a3afee.js" as="script"><link rel="prefetch" href="/assets/js/10.8cf3be2c.js"><link rel="prefetch" href="/assets/js/100.74f35ab8.js"><link rel="prefetch" href="/assets/js/101.7a062346.js"><link rel="prefetch" href="/assets/js/102.c9485235.js"><link rel="prefetch" href="/assets/js/103.d88a3805.js"><link rel="prefetch" href="/assets/js/104.6e034144.js"><link rel="prefetch" href="/assets/js/105.d22f7450.js"><link rel="prefetch" href="/assets/js/106.a6cb54b0.js"><link rel="prefetch" href="/assets/js/107.7b65e72b.js"><link rel="prefetch" href="/assets/js/108.eb5804bb.js"><link rel="prefetch" href="/assets/js/109.05f775e5.js"><link rel="prefetch" href="/assets/js/11.c54ae13c.js"><link rel="prefetch" href="/assets/js/110.51d3d641.js"><link rel="prefetch" href="/assets/js/111.022b64a7.js"><link rel="prefetch" href="/assets/js/112.da8afd52.js"><link rel="prefetch" href="/assets/js/113.05a17b18.js"><link rel="prefetch" href="/assets/js/114.8960d913.js"><link rel="prefetch" href="/assets/js/115.67919f68.js"><link rel="prefetch" href="/assets/js/116.62b0cd71.js"><link rel="prefetch" href="/assets/js/117.ebac3eff.js"><link rel="prefetch" href="/assets/js/118.ecd629bd.js"><link rel="prefetch" href="/assets/js/119.a09a0897.js"><link rel="prefetch" href="/assets/js/12.60aa3b24.js"><link rel="prefetch" href="/assets/js/120.bf639d3d.js"><link rel="prefetch" href="/assets/js/121.b89d0c8e.js"><link rel="prefetch" href="/assets/js/122.1a75ff83.js"><link rel="prefetch" href="/assets/js/123.d2127132.js"><link rel="prefetch" href="/assets/js/124.2caff9e0.js"><link rel="prefetch" href="/assets/js/125.9b9f966a.js"><link rel="prefetch" href="/assets/js/126.58cdfb3d.js"><link rel="prefetch" href="/assets/js/127.8ef09c53.js"><link rel="prefetch" href="/assets/js/128.efdc2ae4.js"><link rel="prefetch" href="/assets/js/129.e35cbc57.js"><link rel="prefetch" href="/assets/js/13.125c13a0.js"><link rel="prefetch" href="/assets/js/130.f01a55e3.js"><link rel="prefetch" href="/assets/js/131.65205f4a.js"><link rel="prefetch" href="/assets/js/132.f42c5a0a.js"><link rel="prefetch" href="/assets/js/133.9ba468b3.js"><link rel="prefetch" href="/assets/js/134.7b597ba9.js"><link rel="prefetch" href="/assets/js/135.fb828b9a.js"><link rel="prefetch" href="/assets/js/136.3887532f.js"><link rel="prefetch" href="/assets/js/137.549bae01.js"><link rel="prefetch" href="/assets/js/138.db8d423d.js"><link rel="prefetch" href="/assets/js/139.dbaf2267.js"><link rel="prefetch" href="/assets/js/14.bd1d0b0d.js"><link rel="prefetch" href="/assets/js/140.6cb65fdc.js"><link rel="prefetch" href="/assets/js/141.9bd6cc4b.js"><link rel="prefetch" href="/assets/js/142.552db5ed.js"><link rel="prefetch" href="/assets/js/143.2c9f2bf4.js"><link rel="prefetch" href="/assets/js/144.fba98a15.js"><link rel="prefetch" href="/assets/js/145.c42f3a21.js"><link rel="prefetch" href="/assets/js/146.596d4d33.js"><link rel="prefetch" href="/assets/js/147.c48ae5c1.js"><link rel="prefetch" href="/assets/js/148.71064871.js"><link rel="prefetch" href="/assets/js/149.16582d21.js"><link rel="prefetch" href="/assets/js/15.f247873b.js"><link rel="prefetch" href="/assets/js/150.ead09aca.js"><link rel="prefetch" href="/assets/js/151.971fdf4b.js"><link rel="prefetch" href="/assets/js/152.369c9362.js"><link rel="prefetch" href="/assets/js/153.371edd15.js"><link rel="prefetch" href="/assets/js/154.e090b491.js"><link rel="prefetch" href="/assets/js/155.c68bf602.js"><link rel="prefetch" href="/assets/js/156.304aea8d.js"><link rel="prefetch" href="/assets/js/157.83beef7f.js"><link rel="prefetch" href="/assets/js/158.bb1794b0.js"><link rel="prefetch" href="/assets/js/159.2d54e792.js"><link rel="prefetch" href="/assets/js/16.04336c71.js"><link rel="prefetch" href="/assets/js/160.99d56586.js"><link rel="prefetch" href="/assets/js/161.edf660aa.js"><link rel="prefetch" href="/assets/js/162.0b84606e.js"><link rel="prefetch" href="/assets/js/163.b59e0d60.js"><link rel="prefetch" href="/assets/js/164.d9eb8228.js"><link rel="prefetch" href="/assets/js/165.ca624c79.js"><link rel="prefetch" href="/assets/js/166.025b2ba1.js"><link rel="prefetch" href="/assets/js/167.abc982cc.js"><link rel="prefetch" href="/assets/js/168.27ca13dc.js"><link rel="prefetch" href="/assets/js/169.41e753a2.js"><link rel="prefetch" href="/assets/js/17.43b3c1c8.js"><link rel="prefetch" href="/assets/js/170.626319e1.js"><link rel="prefetch" href="/assets/js/171.a221dddf.js"><link rel="prefetch" href="/assets/js/172.464b2361.js"><link rel="prefetch" href="/assets/js/174.116607c2.js"><link rel="prefetch" href="/assets/js/175.ea3e8659.js"><link rel="prefetch" href="/assets/js/176.7d7b8afc.js"><link rel="prefetch" href="/assets/js/177.a6e00aa0.js"><link rel="prefetch" href="/assets/js/178.1f93afaf.js"><link rel="prefetch" href="/assets/js/179.3aa00dcd.js"><link rel="prefetch" href="/assets/js/18.d81b44d5.js"><link rel="prefetch" href="/assets/js/180.f8b2b75a.js"><link rel="prefetch" href="/assets/js/181.8e11258a.js"><link rel="prefetch" href="/assets/js/182.22243941.js"><link rel="prefetch" href="/assets/js/183.d051fdf6.js"><link rel="prefetch" href="/assets/js/184.a994075e.js"><link rel="prefetch" href="/assets/js/185.776c7e16.js"><link rel="prefetch" href="/assets/js/186.f1887955.js"><link rel="prefetch" href="/assets/js/187.da0d3626.js"><link rel="prefetch" href="/assets/js/188.8dfc358f.js"><link rel="prefetch" href="/assets/js/189.dcac5a59.js"><link rel="prefetch" href="/assets/js/19.1b3d66e1.js"><link rel="prefetch" href="/assets/js/190.c7e413d0.js"><link rel="prefetch" href="/assets/js/191.d9806121.js"><link rel="prefetch" href="/assets/js/192.869791da.js"><link rel="prefetch" href="/assets/js/193.2d74c4c8.js"><link rel="prefetch" href="/assets/js/194.c73a1909.js"><link rel="prefetch" href="/assets/js/195.e8c74834.js"><link rel="prefetch" href="/assets/js/20.bd5949ec.js"><link rel="prefetch" href="/assets/js/21.3fcf98cf.js"><link rel="prefetch" href="/assets/js/22.2fa1e2e8.js"><link rel="prefetch" href="/assets/js/23.1ae64bb4.js"><link rel="prefetch" href="/assets/js/24.7bdf7387.js"><link rel="prefetch" href="/assets/js/25.392c436e.js"><link rel="prefetch" href="/assets/js/26.58acbd4b.js"><link rel="prefetch" href="/assets/js/27.c725bdd5.js"><link rel="prefetch" href="/assets/js/28.6c9bda1e.js"><link rel="prefetch" href="/assets/js/29.e656b537.js"><link rel="prefetch" href="/assets/js/30.2c326fc7.js"><link rel="prefetch" href="/assets/js/31.e6c9fa30.js"><link rel="prefetch" href="/assets/js/32.c9c88437.js"><link rel="prefetch" href="/assets/js/33.0c53373c.js"><link rel="prefetch" href="/assets/js/34.9821e543.js"><link rel="prefetch" href="/assets/js/35.de8253eb.js"><link rel="prefetch" href="/assets/js/36.d182f929.js"><link rel="prefetch" href="/assets/js/37.9fa79014.js"><link rel="prefetch" href="/assets/js/38.9bebff76.js"><link rel="prefetch" href="/assets/js/39.19a3a2d4.js"><link rel="prefetch" href="/assets/js/4.564edb9d.js"><link rel="prefetch" href="/assets/js/40.cca6955f.js"><link rel="prefetch" href="/assets/js/41.854cd09a.js"><link rel="prefetch" href="/assets/js/42.ca7b612f.js"><link rel="prefetch" href="/assets/js/43.87027d58.js"><link rel="prefetch" href="/assets/js/44.8c2b4f4b.js"><link rel="prefetch" href="/assets/js/45.dffb4e08.js"><link rel="prefetch" href="/assets/js/46.f58049a5.js"><link rel="prefetch" href="/assets/js/47.6854070c.js"><link rel="prefetch" href="/assets/js/48.6cd9fa3d.js"><link rel="prefetch" href="/assets/js/49.e8861afa.js"><link rel="prefetch" href="/assets/js/5.5c31d62f.js"><link rel="prefetch" href="/assets/js/50.703bffab.js"><link rel="prefetch" href="/assets/js/51.6655c373.js"><link rel="prefetch" href="/assets/js/52.deb2eb09.js"><link rel="prefetch" href="/assets/js/53.6e0ed77d.js"><link rel="prefetch" href="/assets/js/54.b05c58ad.js"><link rel="prefetch" href="/assets/js/55.49c8164e.js"><link rel="prefetch" href="/assets/js/56.a5574e6b.js"><link rel="prefetch" href="/assets/js/57.58cb0de4.js"><link rel="prefetch" href="/assets/js/58.52345112.js"><link rel="prefetch" href="/assets/js/59.663ce78d.js"><link rel="prefetch" href="/assets/js/6.a9df34ee.js"><link rel="prefetch" href="/assets/js/60.f06adde2.js"><link rel="prefetch" href="/assets/js/61.170255a1.js"><link rel="prefetch" href="/assets/js/62.9d120050.js"><link rel="prefetch" href="/assets/js/63.70cced6b.js"><link rel="prefetch" href="/assets/js/64.577f3548.js"><link rel="prefetch" href="/assets/js/65.c037b29d.js"><link rel="prefetch" href="/assets/js/66.7dd1045f.js"><link rel="prefetch" href="/assets/js/67.d3aa4d6c.js"><link rel="prefetch" href="/assets/js/68.526dbb61.js"><link rel="prefetch" href="/assets/js/69.58269266.js"><link rel="prefetch" href="/assets/js/7.6609d4d6.js"><link rel="prefetch" href="/assets/js/70.64108f1b.js"><link rel="prefetch" href="/assets/js/71.1e95e0a6.js"><link rel="prefetch" href="/assets/js/72.42e7ec94.js"><link rel="prefetch" href="/assets/js/73.dad4e1c5.js"><link rel="prefetch" href="/assets/js/74.28ea286a.js"><link rel="prefetch" href="/assets/js/75.dd6d4c6f.js"><link rel="prefetch" href="/assets/js/76.ca6539df.js"><link rel="prefetch" href="/assets/js/77.feb13b0e.js"><link rel="prefetch" href="/assets/js/78.321e90e6.js"><link rel="prefetch" href="/assets/js/79.68eb8fcf.js"><link rel="prefetch" href="/assets/js/8.396d51fd.js"><link rel="prefetch" href="/assets/js/80.4edb5321.js"><link rel="prefetch" href="/assets/js/81.735d7e57.js"><link rel="prefetch" href="/assets/js/82.fa120bdf.js"><link rel="prefetch" href="/assets/js/83.bf755f94.js"><link rel="prefetch" href="/assets/js/84.9b32070c.js"><link rel="prefetch" href="/assets/js/85.592aca7c.js"><link rel="prefetch" href="/assets/js/86.4dcd9e73.js"><link rel="prefetch" href="/assets/js/87.a9e546aa.js"><link rel="prefetch" href="/assets/js/88.2a423212.js"><link rel="prefetch" href="/assets/js/89.5f455115.js"><link rel="prefetch" href="/assets/js/9.adb074c6.js"><link rel="prefetch" href="/assets/js/90.5202da0a.js"><link rel="prefetch" href="/assets/js/91.02cee99d.js"><link rel="prefetch" href="/assets/js/92.f16bad0b.js"><link rel="prefetch" href="/assets/js/93.f933634f.js"><link rel="prefetch" href="/assets/js/94.8e7b1d65.js"><link rel="prefetch" href="/assets/js/95.ee0e4a0a.js"><link rel="prefetch" href="/assets/js/96.e21d78c2.js"><link rel="prefetch" href="/assets/js/97.c87e514e.js"><link rel="prefetch" href="/assets/js/98.d123ac92.js"><link rel="prefetch" href="/assets/js/99.92d1b416.js">
    <link rel="stylesheet" href="/assets/css/0.styles.91f57736.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container no-sidebar" data-v-3ba18f14><div data-v-3ba18f14><div id="loader-wrapper" class="loading-wrapper" data-v-041fef5b data-v-3ba18f14 data-v-3ba18f14><div class="loader-main" data-v-041fef5b><div data-v-041fef5b></div><div data-v-041fef5b></div><div data-v-041fef5b></div><div data-v-041fef5b></div></div> <!----> <!----></div> <div class="password-shadow password-wrapper-out" style="display:none;" data-v-68139a52 data-v-3ba18f14 data-v-3ba18f14><h3 class="title" style="display:none;" data-v-68139a52 data-v-68139a52>JavaKeeper</h3> <!----> <label id="box" class="inputBox" style="display:none;" data-v-68139a52 data-v-68139a52><input type="password" value="" data-v-68139a52> <span data-v-68139a52>Konck! Knock!</span> <button data-v-68139a52>OK</button></label> <div class="footer" style="display:none;" data-v-68139a52 data-v-68139a52><span data-v-68139a52><i class="iconfont reco-theme" data-v-68139a52></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-68139a52>vuePress-theme-reco</a></span> <span data-v-68139a52><i class="iconfont reco-copyright" data-v-68139a52></i> <a data-v-68139a52><span data-v-68139a52>海星</span>
            
          <!---->
          2020
        </a></span></div></div> <div class="hide" data-v-3ba18f14><header class="navbar" data-v-3ba18f14><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><!----> <span class="site-name">JavaKeeper</span></a> <div class="links"><div class="color-picker"><a class="color-button"><i class="iconfont reco-color"></i></a> <div class="color-picker-menu" style="display:none;"><div class="mode-options"><h4 class="title">Choose mode</h4> <ul class="color-mode-options"><li class="dark">dark</li><li class="auto active">auto</li><li class="light">light</li></ul></div></div></div> <div class="search-box"><i class="iconfont reco-search"></i> <input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/java/" class="nav-link"><i class="iconfont undefined"></i>
  Java
</a></div><div class="nav-item"><a href="/data-structure-algorithms/" class="nav-link"><i class="iconfont undefined"></i>
  数据结构与算法
</a></div><div class="nav-item"><a href="/data-store/" class="nav-link"><i class="iconfont undefined"></i>
  数据存储与缓存
</a></div><div class="nav-item"><a href="/interview/" class="nav-link"><i class="iconfont undefined"></i>
  直击面试
</a></div> <a href="https://github.com/Jstarfish/JavaKeeper" target="_blank" rel="noopener noreferrer" class="repo-link"><i class="iconfont reco-github"></i>
    GitHub
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav></div></header> <div class="sidebar-mask" data-v-3ba18f14></div> <aside class="sidebar" data-v-3ba18f14><div class="personal-info-wrapper" data-v-5f6acefd data-v-3ba18f14><!----> <h3 class="name" data-v-5f6acefd>
    海星
  </h3> <div class="num" data-v-5f6acefd><div data-v-5f6acefd><h3 data-v-5f6acefd>0</h3> <h6 data-v-5f6acefd>Article</h6></div> <div data-v-5f6acefd><h3 data-v-5f6acefd>0</h3> <h6 data-v-5f6acefd>Tag</h6></div></div> <hr data-v-5f6acefd></div> <nav class="nav-links"><div class="nav-item"><a href="/java/" class="nav-link"><i class="iconfont undefined"></i>
  Java
</a></div><div class="nav-item"><a href="/data-structure-algorithms/" class="nav-link"><i class="iconfont undefined"></i>
  数据结构与算法
</a></div><div class="nav-item"><a href="/data-store/" class="nav-link"><i class="iconfont undefined"></i>
  数据存储与缓存
</a></div><div class="nav-item"><a href="/interview/" class="nav-link"><i class="iconfont undefined"></i>
  直击面试
</a></div> <a href="https://github.com/Jstarfish/JavaKeeper" target="_blank" rel="noopener noreferrer" class="repo-link"><i class="iconfont reco-github"></i>
    GitHub
    <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a></nav> <!----> </aside> <div class="password-shadow password-wrapper-in" style="display:none;" data-v-68139a52 data-v-3ba18f14><h3 class="title" style="display:none;" data-v-68139a52 data-v-68139a52></h3> <!----> <label id="box" class="inputBox" style="display:none;" data-v-68139a52 data-v-68139a52><input type="password" value="" data-v-68139a52> <span data-v-68139a52>Konck! Knock!</span> <button data-v-68139a52>OK</button></label> <div class="footer" style="display:none;" data-v-68139a52 data-v-68139a52><span data-v-68139a52><i class="iconfont reco-theme" data-v-68139a52></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-68139a52>vuePress-theme-reco</a></span> <span data-v-68139a52><i class="iconfont reco-copyright" data-v-68139a52></i> <a data-v-68139a52><span data-v-68139a52>海星</span>
            
          <!---->
          2020
        </a></span></div></div> <div data-v-3ba18f14><main class="page"><div class="page-title" style="display:none;"><h1 class="title"></h1> <div data-v-5d8dbdb4><i class="iconfont reco-account" data-v-5d8dbdb4><span data-v-5d8dbdb4>海星</span></i> <!----> <!----> <!----></div></div> <div class="theme-reco-content content__default" style="display:none;"><p><img src="https://imgkr.cn-bj.ufileos.com/d7e94860-00bf-4cd0-ae0b-11bad05a32ca.jpg" alt=""></p> <p>https://www.cnblogs.com/jurendage/p/11255197.html</p> <h2 id="背景"><a href="#背景" class="header-anchor">#</a> 背景</h2> <p>软件开发过程中，不可避免的是需要处理各种异常，就我自己来说，至少有一半以上的时间都是在处理各种异常情况，所以代码中就会出现大量的<code>try {...} catch {...} finally {...}</code> 代码块，不仅有大量的冗余代码，而且还影响代码的可读性。比较下面两张图，看看您现在编写的代码属于哪一种风格？然后哪种编码风格您更喜欢？</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-8ca7621c2251f707.png" alt="丑陋的 try catch 代码块"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-4a8ea48d096575f0.png" alt="优雅的Controller"></p> <p>上面的示例，还只是在<code>Controller</code>层，如果是在<code>Service</code>层，可能会有更多的<code>try catch</code>代码块。这将会严重影响代码的可读性、“美观性”。</p> <p>所以如果是我的话，我肯定偏向于第二种，我可以把更多的精力放在业务代码的开发，同时代码也会变得更加简洁。</p> <p>既然业务代码不显式地对异常进行捕获、处理，而异常肯定还是处理的，不然系统岂不是动不动就崩溃了，所以必须得有其他地方捕获并处理这些异常。</p> <p>那么问题来了，如何优雅的处理各种异常？</p> <h2 id="什么是统一异常处理"><a href="#什么是统一异常处理" class="header-anchor">#</a> 什么是统一异常处理</h2> <p><code>Spring</code>在3.2版本增加了一个注解<code>@ControllerAdvice</code>，可以与<code>@ExceptionHandler</code>、<code>@InitBinder</code>、<code>@ModelAttribute</code> 等注解注解配套使用，对于这几个注解的作用，这里不做过多赘述，若有不了解的，可以参考Spring3.2新注解@ControllerAdvice，先大概有个了解。</p> <p>不过跟异常处理相关的只有注解<code>@ExceptionHandler</code>，从字面上看，就是 <strong>异常处理器</strong> 的意思，其实际作用也是：若在某个<code>Controller</code>类定义一个异常处理方法，并在方法上添加该注解，那么当出现指定的异常时，会执行该处理异常的方法，其可以使用springmvc提供的数据绑定，比如注入HttpServletRequest等，还可以接受一个当前抛出的Throwable对象。</p> <p>但是，这样一来，就必须在每一个<code>Controller</code>类都定义一套这样的异常处理方法，因为异常可以是各种各样。这样一来，就会造成大量的冗余代码，而且若需要新增一种异常的处理逻辑，就必须修改所有<code>Controller</code>类了，很不优雅。</p> <p>当然你可能会说，那就定义个类似<code>BaseController</code>的基类，这样总行了吧。</p> <p>这种做法虽然没错，但仍不尽善尽美，因为这样的代码有一定的侵入性和耦合性。简简单单的<code>Controller</code>，我为啥非得继承这样一个类呢，万一已经继承其他基类了呢。大家都知道<code>Java</code>只能继承一个类。</p> <p>那有没有一种方案，既不需要跟<code>Controller</code>耦合，也可以将定义的 <strong>异常处理器</strong> 应用到所有控制器呢？所以注解<code>@ControllerAdvice</code>出现了，简单的说，该注解可以把异常处理器应用到所有控制器，而不是单个控制器。借助该注解，我们可以实现：在独立的某个地方，比如单独一个类，定义一套对各种异常的处理机制，然后在类的签名加上注解<code>@ControllerAdvice</code>，统一对 <code>不同阶段的</code>、<code>不同异常</code> 进行处理。这就是统一异常处理的原理。</p> <p>注意到上面对异常按阶段进行分类，大体可以分成：进入<code>Controller</code>前的异常 和 <code>Service</code> 层异常，具体可以参考下图：</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-dcb353db49254277.jpg" alt="不同阶段的异常"></p> <h2 id="目标"><a href="#目标" class="header-anchor">#</a> 目标</h2> <p>消灭95%以上的 <code>try catch</code> 代码块，以优雅的 <code>Assert</code>(断言) 方式来校验业务的异常情况，只关注业务逻辑，而不用花费大量精力写冗余的 <code>try catch</code> 代码块。</p> <h2 id="统一异常处理实战"><a href="#统一异常处理实战" class="header-anchor">#</a> 统一异常处理实战</h2> <p>在定义统一异常处理类之前，先来介绍一下如何优雅的判定异常情况并抛异常。</p> <h3 id="用-assert-断言-替换-throw-exception"><a href="#用-assert-断言-替换-throw-exception" class="header-anchor">#</a> 用 Assert(断言) 替换 throw exception</h3> <p>想必 <code>Assert(断言)</code> 大家都很熟悉，比如 <code>Spring</code> 家族的 <code>org.springframework.util.Assert</code>，在我们写测试用例的时候经常会用到，使用断言能让我们编码的时候有一种非一般丝滑的感觉，比如：</p> <div class="language-java extra-class"><pre class="language-java"><code>    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">test1</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
        <span class="token class-name">User</span> user <span class="token operator">=</span> userDao<span class="token punctuation">.</span><span class="token function">selectById</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">Assert</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span>user<span class="token punctuation">,</span> <span class="token string">&quot;用户不存在.&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Test</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">test2</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 另一种写法</span>
        <span class="token class-name">User</span> user <span class="token operator">=</span> userDao<span class="token punctuation">.</span><span class="token function">selectById</span><span class="token punctuation">(</span>userId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>user <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">&quot;用户不存在.&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
</code></pre></div><p>有没有感觉第一种判定非空的写法很优雅，第二种写法则是相对丑陋的 <code>if {...}</code> 代码块。那么
神奇的 <code>Assert.notNull()</code> 背后到底做了什么呢？下面是 <code>Assert</code> 的部分源码：</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name">Assert</span> <span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token class-name">Assert</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">notNull</span><span class="token punctuation">(</span><span class="token annotation punctuation">@Nullable</span> <span class="token class-name">Object</span> object<span class="token punctuation">,</span> <span class="token class-name">String</span> message<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>object <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>可以看到，<code>Assert</code> 其实就是帮我们把 <code>if {...}</code> 封装了一下，是不是很神奇。虽然很简单，但不可否认的是编码体验至少提升了一个档次。那么我们能不能模仿<code>org.springframework.util.Assert</code>，也写一个断言类，不过断言失败后抛出的异常不是<code>IllegalArgumentException</code> 这些内置异常，而是我们自己定义的异常。下面让我们来尝试一下。</p> <h4 id="assert"><a href="#assert" class="header-anchor">#</a> Assert</h4> <div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Assert</span> <span class="token punctuation">{</span>
    <span class="token comment">/**
     * 创建异常
     * @param args
     * @return
     */</span>
    <span class="token class-name">BaseException</span> <span class="token function">newException</span><span class="token punctuation">(</span><span class="token class-name">Object</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">/**
     * 创建异常
     * @param t
     * @param args
     * @return
     */</span>
    <span class="token class-name">BaseException</span> <span class="token function">newException</span><span class="token punctuation">(</span><span class="token class-name">Throwable</span> t<span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">/**
     * &lt;p&gt;断言对象&lt;code&gt;obj&lt;/code&gt;非空。如果对象&lt;code&gt;obj&lt;/code&gt;为空，则抛出异常
     *
     * @param obj 待判断对象
     */</span>
    <span class="token keyword">default</span> <span class="token keyword">void</span> <span class="token function">assertNotNull</span><span class="token punctuation">(</span><span class="token class-name">Object</span> obj<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>obj <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token function">newException</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * &lt;p&gt;断言对象&lt;code&gt;obj&lt;/code&gt;非空。如果对象&lt;code&gt;obj&lt;/code&gt;为空，则抛出异常
     * &lt;p&gt;异常信息&lt;code&gt;message&lt;/code&gt;支持传递参数方式，避免在判断之前进行字符串拼接操作
     *
     * @param obj 待判断对象
     * @param args message占位符对应的参数列表
     */</span>
    <span class="token keyword">default</span> <span class="token keyword">void</span> <span class="token function">assertNotNull</span><span class="token punctuation">(</span><span class="token class-name">Object</span> obj<span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>obj <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">throw</span> <span class="token function">newException</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>上面的<code>Assert</code>断言方法是使用接口的默认方法定义的，然后有没有发现当断言失败后，抛出的异常不是具体的某个异常，而是交由2个<code>newException</code>接口方法提供。因为业务逻辑中出现的异常基本都是对应特定的场景，比如根据用户id获取用户信息，查询结果为<code>null</code>，此时抛出的异常可能为<code>UserNotFoundException</code>，并且有特定的异常码（比如7001）和异常信息“用户不存在”。所以具体抛出什么异常，有<code>Assert</code>的实现类决定。</p> <p>看到这里，您可能会有这样的疑问，按照上面的说法，那岂不是有多少异常情况，就得有定义等量的断言类和异常类，这显然是反人类的，这也没想象中高明嘛。别急，且听我细细道来。</p> <h4 id="善解人意的enum"><a href="#善解人意的enum" class="header-anchor">#</a> 善解人意的Enum</h4> <p>自定义异常<code>BaseException</code>有2个属性，即<code>code</code>、<code>message</code>，这样一对属性，有没有想到什么类一般也会定义这2个属性？没错，就是枚举类。且看我如何将 <code>Enum</code> 和 <code>Assert</code> 结合起来，相信我一定会让你眼前一亮。如下：</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">IResponseEnum</span> <span class="token punctuation">{</span>
    <span class="token keyword">int</span> <span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token class-name">String</span> <span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/**
 * &lt;p&gt;业务异常&lt;/p&gt;
 * &lt;p&gt;业务处理时，出现异常，可以抛出该异常&lt;/p&gt;
 */</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">BusinessException</span> <span class="token keyword">extends</span>  <span class="token class-name">BaseException</span> <span class="token punctuation">{</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">long</span> serialVersionUID <span class="token operator">=</span> <span class="token number">1L</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token class-name">BusinessException</span><span class="token punctuation">(</span><span class="token class-name">IResponseEnum</span> responseEnum<span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">,</span> <span class="token class-name">String</span> message<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">super</span><span class="token punctuation">(</span>responseEnum<span class="token punctuation">,</span> args<span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token class-name">BusinessException</span><span class="token punctuation">(</span><span class="token class-name">IResponseEnum</span> responseEnum<span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">,</span> <span class="token class-name">String</span> message<span class="token punctuation">,</span> <span class="token class-name">Throwable</span> cause<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">super</span><span class="token punctuation">(</span>responseEnum<span class="token punctuation">,</span> args<span class="token punctuation">,</span> message<span class="token punctuation">,</span> cause<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">BusinessExceptionAssert</span> <span class="token keyword">extends</span> <span class="token class-name">IResponseEnum</span><span class="token punctuation">,</span> <span class="token class-name">Assert</span> <span class="token punctuation">{</span>

    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">default</span> <span class="token class-name">BaseException</span> <span class="token function">newException</span><span class="token punctuation">(</span><span class="token class-name">Object</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">String</span> msg <span class="token operator">=</span> <span class="token class-name">MessageFormat</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">BusinessException</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> args<span class="token punctuation">,</span> msg<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token annotation punctuation">@Override</span>
    <span class="token keyword">default</span> <span class="token class-name">BaseException</span> <span class="token function">newException</span><span class="token punctuation">(</span><span class="token class-name">Throwable</span> t<span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">String</span> msg <span class="token operator">=</span> <span class="token class-name">MessageFormat</span><span class="token punctuation">.</span><span class="token function">format</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">BusinessException</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> args<span class="token punctuation">,</span> msg<span class="token punctuation">,</span> t<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
<span class="token annotation punctuation">@Getter</span>
<span class="token annotation punctuation">@AllArgsConstructor</span>
<span class="token keyword">public</span> <span class="token keyword">enum</span> <span class="token class-name">ResponseEnum</span> <span class="token keyword">implements</span> <span class="token class-name">BusinessExceptionAssert</span> <span class="token punctuation">{</span>

    <span class="token comment">/**
     * Bad licence type
     */</span>
    <span class="token function">BAD_LICENCE_TYPE</span><span class="token punctuation">(</span><span class="token number">7001</span><span class="token punctuation">,</span> <span class="token string">&quot;Bad licence type.&quot;</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token comment">/**
     * Licence not found
     */</span>
    <span class="token function">LICENCE_NOT_FOUND</span><span class="token punctuation">(</span><span class="token number">7002</span><span class="token punctuation">,</span> <span class="token string">&quot;Licence not found.&quot;</span><span class="token punctuation">)</span>
    <span class="token punctuation">;</span>

    <span class="token comment">/**
     * 返回码
     */</span>
    <span class="token keyword">private</span> <span class="token keyword">int</span> code<span class="token punctuation">;</span>
    <span class="token comment">/**
     * 返回消息
     */</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> message<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>看到这里，有没有眼前一亮的感觉，代码示例中定义了两个枚举实例：<code>BAD_LICENCE_TYPE</code>、<code>LICENCE_NOT_FOUND</code>，分别对应了<code>BadLicenceTypeException</code>、<code>LicenceNotFoundException</code>两种异常。以后每增加一种异常情况，只需增加一个枚举实例即可，再也不用每一种异常都定义一个异常类了。然后再来看下如何使用，假设<code>LicenceService</code>有校验<code>Licence</code>是否存在的方法，如下：</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token comment">/**
 * 校验{@link Licence}存在
 * @param licence
 */</span>
<span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">checkNotNull</span><span class="token punctuation">(</span><span class="token class-name">Licence</span> licence<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token class-name">ResponseEnum</span><span class="token punctuation">.</span>LICENCE_NOT_FOUND<span class="token punctuation">.</span><span class="token function">assertNotNull</span><span class="token punctuation">(</span>licence<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>若不使用断言，代码可能如下：</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">checkNotNull</span><span class="token punctuation">(</span><span class="token class-name">Licence</span> licence<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>licence <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">LicenceNotFoundException</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 或者这样</span>
        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BusinessException</span><span class="token punctuation">(</span><span class="token number">7001</span><span class="token punctuation">,</span> <span class="token string">&quot;Bad licence type.&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>使用枚举类结合(继承)<code>Assert</code>，只需根据特定的异常情况定义不同的枚举实例，如上面的<code>BAD_LICENCE_TYPE</code>、<code>LICENCE_NOT_FOUND</code>，就能够针对不同情况抛出特定的异常（这里指携带特定的异常码和异常消息），这样既不用定义大量的异常类，同时还具备了断言的良好可读性，当然这种方案的好处远不止这些，请继续阅读后文，慢慢体会。</p> <blockquote><p>注：上面举的例子是针对特定的业务，而有部分异常情况是通用的，比如：服务器繁忙、网络异常、服务器异常、参数校验异常、404等，所以有<code>CommonResponseEnum</code>、<code>ArgumentResponseEnum</code>、<code>ServletResponseEnum</code>，其中 <code>ServletResponseEnum</code> 会在后文详细说明。</p></blockquote> <h3 id="定义统一异常处理器类"><a href="#定义统一异常处理器类" class="header-anchor">#</a> 定义统一异常处理器类</h3> <div class="language-java extra-class"><pre class="language-java"><code><span class="token annotation punctuation">@Slf4j</span>
<span class="token annotation punctuation">@Component</span>
<span class="token annotation punctuation">@ControllerAdvice</span>
<span class="token annotation punctuation">@ConditionalOnWebApplication</span>
<span class="token annotation punctuation">@ConditionalOnMissingBean</span><span class="token punctuation">(</span><span class="token class-name">UnifiedExceptionHandler</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UnifiedExceptionHandler</span> <span class="token punctuation">{</span>
    <span class="token comment">/**
     * 生产环境
     */</span>
    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token keyword">static</span> <span class="token class-name">String</span> ENV_PROD <span class="token operator">=</span> <span class="token string">&quot;prod&quot;</span><span class="token punctuation">;</span> 

    <span class="token annotation punctuation">@Autowired</span>
    <span class="token keyword">private</span> <span class="token class-name">UnifiedMessageSource</span> unifiedMessageSource<span class="token punctuation">;</span>

    <span class="token comment">/**
     * 当前环境
     */</span>
    <span class="token annotation punctuation">@Value</span><span class="token punctuation">(</span><span class="token string">&quot;${spring.profiles.active}&quot;</span><span class="token punctuation">)</span>
    <span class="token keyword">private</span> <span class="token class-name">String</span> profile<span class="token punctuation">;</span>
    
    <span class="token comment">/**
     * 获取国际化消息
     *
     * @param e 异常
     * @return
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token class-name">BaseException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">String</span> code <span class="token operator">=</span> <span class="token string">&quot;response.&quot;</span> <span class="token operator">+</span> e<span class="token punctuation">.</span><span class="token function">getResponseEnum</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">String</span> message <span class="token operator">=</span> unifiedMessageSource<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span>code<span class="token punctuation">,</span> e<span class="token punctuation">.</span><span class="token function">getArgs</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>message <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> message<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span> e<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> message<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * 业务异常
     *
     * @param e 异常
     * @return 异常结果
     */</span>
    <span class="token annotation punctuation">@ExceptionHandler</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token class-name">BusinessException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token annotation punctuation">@ResponseBody</span>
    <span class="token keyword">public</span> <span class="token class-name">ErrorResponse</span> <span class="token function">handleBusinessException</span><span class="token punctuation">(</span><span class="token class-name">BaseException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ErrorResponse</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token function">getResponseEnum</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">getMessage</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * 自定义异常
     *
     * @param e 异常
     * @return 异常结果
     */</span>
    <span class="token annotation punctuation">@ExceptionHandler</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token class-name">BaseException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token annotation punctuation">@ResponseBody</span>
    <span class="token keyword">public</span> <span class="token class-name">ErrorResponse</span> <span class="token function">handleBaseException</span><span class="token punctuation">(</span><span class="token class-name">BaseException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ErrorResponse</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token function">getResponseEnum</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">getMessage</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * Controller上一层相关异常
     *
     * @param e 异常
     * @return 异常结果
     */</span>
    <span class="token annotation punctuation">@ExceptionHandler</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
            <span class="token class-name">NoHandlerFoundException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">HttpRequestMethodNotSupportedException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">HttpMediaTypeNotSupportedException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">MissingPathVariableException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">MissingServletRequestParameterException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">TypeMismatchException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">HttpMessageNotReadableException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> 
            <span class="token class-name">HttpMessageNotWritableException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token comment">// BindException.class,</span>
            <span class="token comment">// MethodArgumentNotValidException.class</span>
            <span class="token class-name">HttpMediaTypeNotAcceptableException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">ServletRequestBindingException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">ConversionNotSupportedException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">MissingServletRequestPartException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span>
            <span class="token class-name">AsyncRequestTimeoutException</span><span class="token punctuation">.</span><span class="token keyword">class</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token annotation punctuation">@ResponseBody</span>
    <span class="token keyword">public</span> <span class="token class-name">ErrorResponse</span> <span class="token function">handleServletException</span><span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">int</span> code <span class="token operator">=</span> <span class="token class-name">CommonResponseEnum</span><span class="token punctuation">.</span>SERVER_ERROR<span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">try</span> <span class="token punctuation">{</span>
            <span class="token class-name">ServletResponseEnum</span> servletExceptionEnum <span class="token operator">=</span> <span class="token class-name">ServletResponseEnum</span><span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getSimpleName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            code <span class="token operator">=</span> servletExceptionEnum<span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IllegalArgumentException</span> e1<span class="token punctuation">)</span> <span class="token punctuation">{</span>
            log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">&quot;class [{}] not defined in enum {}&quot;</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">ServletResponseEnum</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>ENV_PROD<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>profile<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// 当为生产环境, 不适合把具体的异常信息展示给用户, 比如404.</span>
            code <span class="token operator">=</span> <span class="token class-name">CommonResponseEnum</span><span class="token punctuation">.</span>SERVER_ERROR<span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">BaseException</span> baseException <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BaseException</span><span class="token punctuation">(</span><span class="token class-name">CommonResponseEnum</span><span class="token punctuation">.</span>SERVER_ERROR<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">String</span> message <span class="token operator">=</span> <span class="token function">getMessage</span><span class="token punctuation">(</span>baseException<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ErrorResponse</span><span class="token punctuation">(</span>code<span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ErrorResponse</span><span class="token punctuation">(</span>code<span class="token punctuation">,</span> e<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>


    <span class="token comment">/**
     * 参数绑定异常
     *
     * @param e 异常
     * @return 异常结果
     */</span>
    <span class="token annotation punctuation">@ExceptionHandler</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token class-name">BindException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token annotation punctuation">@ResponseBody</span>
    <span class="token keyword">public</span> <span class="token class-name">ErrorResponse</span> <span class="token function">handleBindException</span><span class="token punctuation">(</span><span class="token class-name">BindException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">&quot;参数绑定校验异常&quot;</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">wrapperBindingResult</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token function">getBindingResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * 参数校验异常，将校验失败的所有异常组合成一条错误信息
     *
     * @param e 异常
     * @return 异常结果
     */</span>
    <span class="token annotation punctuation">@ExceptionHandler</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token class-name">MethodArgumentNotValidException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token annotation punctuation">@ResponseBody</span>
    <span class="token keyword">public</span> <span class="token class-name">ErrorResponse</span> <span class="token function">handleValidException</span><span class="token punctuation">(</span><span class="token class-name">MethodArgumentNotValidException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">&quot;参数绑定校验异常&quot;</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">wrapperBindingResult</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token function">getBindingResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * 包装绑定异常结果
     *
     * @param bindingResult 绑定结果
     * @return 异常结果
     */</span>
    <span class="token keyword">private</span> <span class="token class-name">ErrorResponse</span> <span class="token function">wrapperBindingResult</span><span class="token punctuation">(</span><span class="token class-name">BindingResult</span> bindingResult<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">StringBuilder</span> msg <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">ObjectError</span> error <span class="token operator">:</span> bindingResult<span class="token punctuation">.</span><span class="token function">getAllErrors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            msg<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">&quot;, &quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span>error <span class="token keyword">instanceof</span> <span class="token class-name">FieldError</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
                msg<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">FieldError</span><span class="token punctuation">)</span> error<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getField</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">&quot;: &quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            msg<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>error<span class="token punctuation">.</span><span class="token function">getDefaultMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">?</span> <span class="token string">&quot;&quot;</span> <span class="token operator">:</span> error<span class="token punctuation">.</span><span class="token function">getDefaultMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ErrorResponse</span><span class="token punctuation">(</span><span class="token class-name">ArgumentResponseEnum</span><span class="token punctuation">.</span>VALID_ERROR<span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> msg<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * 未定义异常
     *
     * @param e 异常
     * @return 异常结果
     */</span>
    <span class="token annotation punctuation">@ExceptionHandler</span><span class="token punctuation">(</span>value <span class="token operator">=</span> <span class="token class-name">Exception</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token annotation punctuation">@ResponseBody</span>
    <span class="token keyword">public</span> <span class="token class-name">ErrorResponse</span> <span class="token function">handleException</span><span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>ENV_PROD<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>profile<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token comment">// 当为生产环境, 不适合把具体的异常信息展示给用户, 比如数据库异常信息.</span>
            <span class="token keyword">int</span> code <span class="token operator">=</span> <span class="token class-name">CommonResponseEnum</span><span class="token punctuation">.</span>SERVER_ERROR<span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">BaseException</span> baseException <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BaseException</span><span class="token punctuation">(</span><span class="token class-name">CommonResponseEnum</span><span class="token punctuation">.</span>SERVER_ERROR<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">String</span> message <span class="token operator">=</span> <span class="token function">getMessage</span><span class="token punctuation">(</span>baseException<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ErrorResponse</span><span class="token punctuation">(</span>code<span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ErrorResponse</span><span class="token punctuation">(</span><span class="token class-name">CommonResponseEnum</span><span class="token punctuation">.</span>SERVER_ERROR<span class="token punctuation">.</span><span class="token function">getCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
<span class="token punctuation">}</span>
</code></pre></div><p>可以看到，上面将异常分成几类，实际上只有两大类，一类是<code>ServletException</code>、<code>ServiceException</code>，还记得上文提到的 <strong>按阶段分类</strong> 吗，即对应 进入<code>Controller</code>前的异常 和 <code>Service</code> 层异常；然后 <code>ServiceException</code> 再分成自定义异常、未知异常。对应关系如下：</p> <ul><li>进入<code>Controller</code>前的异常: handleServletException、handleBindException、handleValidException</li> <li>自定义异常: handleBusinessException、handleBaseException</li> <li>未知异常: handleException</li></ul> <p>接下来分别对这几种异常处理器做详细说明。</p> <h4 id="异常处理器说明"><a href="#异常处理器说明" class="header-anchor">#</a> 异常处理器说明</h4> <h5 id="handleservletexception"><a href="#handleservletexception" class="header-anchor">#</a> handleServletException</h5> <p>一个<code>http</code>请求，在到达<code>Controller</code>前，会对该请求的请求信息与目标控制器信息做一系列校验。这里简单说一下：</p> <ul><li>NoHandlerFoundException：首先根据请求<code>Url</code>查找有没有对应的控制器，若没有则会抛该异常，也就是大家非常熟悉的<code>404</code>异常；</li> <li>HttpRequestMethodNotSupportedException：若匹配到了（匹配结果是一个列表，不同的是<code>http</code>方法不同，如：Get、Post等），则尝试将请求的<code>http</code>方法与列表的控制器做匹配，若没有对应<code>http</code>方法的控制器，则抛该异常；</li> <li>HttpMediaTypeNotSupportedException： 然后再对请求头与控制器支持的做比较，比如<code>content-type</code>请求头，若控制器的参数签名包含注解<code>@RequestBody</code>，但是请求的<code>content-type</code>请求头的值没有包含<code>application/json</code>，那么会抛该异常（当然，不止这种情况会抛这个异常）；</li> <li>MissingPathVariableException：未检测到路径参数。比如url为：<code>/licence/{licenceId}</code>，参数签名包含<code>@PathVariable(&quot;licenceId&quot;)</code>，当请求的url为<code>/licence</code>，在没有明确定义url为<code>/licence</code>的情况下，会被判定为：缺少路径参数；</li> <li>MissingServletRequestParameterException：缺少请求参数。比如定义了参数@RequestParam(&quot;licenceId&quot;) String licenceId，但发起请求时，未携带该参数，则会抛该异常；</li> <li>TypeMismatchException: 参数类型匹配失败。比如：接收参数为Long型，但传入的值确是一个字符串，那么将会出现类型转换失败的情况，这时会抛该异常；</li> <li>HttpMessageNotReadableException：与上面的<code>HttpMediaTypeNotSupportedException</code>举的例子完全相反，即请求头携带了<code>&quot;content-type: application/json;charset=UTF-8&quot;</code>，但接收参数却没有添加注解<code>@RequestBody</code>，或者请求体携带的 json 串反序列化成 pojo 的过程中失败了，也会抛该异常；</li> <li>HttpMessageNotWritableException：返回的 pojo 在序列化成 json 过程失败了，那么抛该异常；</li> <li>HttpMediaTypeNotAcceptableException：未知；</li> <li>ServletRequestBindingException：未知；</li> <li>ConversionNotSupportedException：未知；</li> <li>MissingServletRequestPartException：未知；</li> <li>AsyncRequestTimeoutException：未知；</li></ul> <h5 id="handlebindexception"><a href="#handlebindexception" class="header-anchor">#</a> handleBindException</h5> <p>参数校验异常，后文详细说明。</p> <h5 id="handlevalidexception"><a href="#handlevalidexception" class="header-anchor">#</a> handleValidException</h5> <p>参数校验异常，后文详细说明。</p> <h5 id="handlebusinessexception、handlebaseexception"><a href="#handlebusinessexception、handlebaseexception" class="header-anchor">#</a> handleBusinessException、handleBaseException</h5> <p>处理自定义的业务异常，只是<code>handleBaseException</code>处理的是除了 <code>BusinessException</code> 意外的所有业务异常。就目前来看，这2个是可以合并成一个的。</p> <h5 id="handleexception"><a href="#handleexception" class="header-anchor">#</a> handleException</h5> <p>处理所有未知的异常，比如操作数据库失败的异常。</p> <blockquote><p>注：上面的<code>handleServletException</code>、<code>handleException</code> 这两个处理器，返回的异常信息，不同环境返回的可能不一样，以为这些异常信息都是框架自带的异常信息，一般都是英文的，不太好直接展示给用户看，所以统一返回<code>SERVER_ERROR</code>代表的异常信息。</p></blockquote> <h4 id="异于常人的404"><a href="#异于常人的404" class="header-anchor">#</a> 异于常人的404</h4> <p>上文提到，当请求没有匹配到控制器的情况下，会抛出<code>NoHandlerFoundException</code>异常，但其实默认情况下不是这样，默认情况下会出现类似如下页面：</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-e6022ae68103153e.png" alt="Whitelabel Error Page"></p> <p>这个页面是如何出现的呢？实际上，当出现404的时候，默认是不抛异常的，而是 <code>forward</code>跳转到<code>/error</code>控制器，<code>spring</code>也提供了默认的<code>error</code>控制器，如下：</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-a1fd0e9af958d8eb.png" alt="BasicErrorController"></p> <p>那么，如何让404也抛出异常呢，只需在<code>properties</code>文件中加入如下配置即可：</p> <div class="language- extra-class"><pre class="language-text"><code>spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
</code></pre></div><p>如此，就可以异常处理器中捕获它了，然后前端只要捕获到特定的状态码，立即跳转到404页面即可</p> <h4 id="统一返回结果"><a href="#统一返回结果" class="header-anchor">#</a> 统一返回结果</h4> <p>在验证统一异常处理器之前，顺便说一下统一返回结果。说白了，其实是统一一下返回结果的数据结构。<code>code</code>、<code>message</code> 是所有返回结果中必有的字段，而当需要返回数据时，则需要另一个字段 <code>data</code> 来表示。</p> <p>所以首先定义一个 <code>BaseResponse</code> 来作为所有返回结果的基类；</p> <p>然后定义一个通用返回结果类<code>CommonResponse</code>，继承 <code>BaseResponse</code>，而且多了字段 <code>data</code>；</p> <p>为了区分成功和失败返回结果，于是再定义一个 <code>ErrorResponse</code>；</p> <p>最后还有一种常见的返回结果，即返回的数据带有分页信息，因为这种接口比较常见，所以有必要单独定义一个返回结果类 <code>QueryDataResponse</code>，该类继承自 <code>CommonResponse</code>，只是把 <code>data</code> 字段的类型限制为 <code>QueryDdata</code>，<code>QueryDdata</code>中定义了分页信息相应的字段，即<code>totalCount</code>、<code>pageNo</code>、 <code>pageSize</code>、<code>records</code>。</p> <p>其中比较常用的只有 <code>CommonResponse</code> 和 <code>QueryDataResponse</code>，但是名字又贼鬼死长，何不定义2个名字超简单的类来替代呢？于是 <code>R</code> 和 <code>QR</code> 诞生了，以后返回结果的时候只需这样写：<code>new R&lt;&gt;(data)</code>、<code>new QR&lt;&gt;(queryData)</code>。</p> <p>所有的返回结果类的定义这里就不贴出来了</p> <h4 id="验证统一异常处理"><a href="#验证统一异常处理" class="header-anchor">#</a> 验证统一异常处理</h4> <p>因为这一套统一异常处理可以说是通用的，所有可以设计成一个 <code>common</code>包，以后每一个新项目/模块只需引入该包即可。所以为了验证，需要新建一个项目，并引入该 <code>common</code>包。</p> <h5 id="主要代码"><a href="#主要代码" class="header-anchor">#</a> 主要代码</h5> <p>下面是用于验证的主要源码：</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token annotation punctuation">@Service</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">LicenceService</span> <span class="token keyword">extends</span> <span class="token class-name">ServiceImpl</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">LicenceMapper</span><span class="token punctuation">,</span> <span class="token class-name">Licence</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">{</span>

    <span class="token annotation punctuation">@Autowired</span>
    <span class="token keyword">private</span> <span class="token class-name">OrganizationClient</span> organizationClient<span class="token punctuation">;</span>

    <span class="token comment">/**
     * 查询{@link Licence} 详情
     * @param licenceId
     * @return
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">LicenceDTO</span> <span class="token function">queryDetail</span><span class="token punctuation">(</span><span class="token class-name">Long</span> licenceId<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">Licence</span> licence <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getById</span><span class="token punctuation">(</span>licenceId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">checkNotNull</span><span class="token punctuation">(</span>licence<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token class-name">OrganizationDTO</span> org <span class="token operator">=</span> <span class="token class-name">ClientUtil</span><span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> organizationClient<span class="token punctuation">.</span><span class="token function">getOrganization</span><span class="token punctuation">(</span>licence<span class="token punctuation">.</span><span class="token function">getOrganizationId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token function">toLicenceDTO</span><span class="token punctuation">(</span>licence<span class="token punctuation">,</span> org<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * 分页获取
     * @param licenceParam 分页查询参数
     * @return
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">QueryData</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">SimpleLicenceDTO</span><span class="token punctuation">&gt;</span></span> <span class="token function">getLicences</span><span class="token punctuation">(</span><span class="token class-name">LicenceParam</span> licenceParam<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">String</span> licenceType <span class="token operator">=</span> licenceParam<span class="token punctuation">.</span><span class="token function">getLicenceType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">LicenceTypeEnum</span> licenceTypeEnum <span class="token operator">=</span> <span class="token class-name">LicenceTypeEnum</span><span class="token punctuation">.</span><span class="token function">parseOfNullable</span><span class="token punctuation">(</span>licenceType<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 断言, 非空</span>
        <span class="token class-name">ResponseEnum</span><span class="token punctuation">.</span>BAD_LICENCE_TYPE<span class="token punctuation">.</span><span class="token function">assertNotNull</span><span class="token punctuation">(</span>licenceTypeEnum<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token class-name">LambdaQueryWrapper</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Licence</span><span class="token punctuation">&gt;</span></span> wrapper <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LambdaQueryWrapper</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        wrapper<span class="token punctuation">.</span><span class="token function">eq</span><span class="token punctuation">(</span><span class="token class-name">Licence</span><span class="token operator">::</span><span class="token function">getLicenceType</span><span class="token punctuation">,</span> licenceType<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">IPage</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Licence</span><span class="token punctuation">&gt;</span></span> page <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">page</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">QueryPage</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>licenceParam<span class="token punctuation">)</span><span class="token punctuation">,</span> wrapper<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">QueryData</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>page<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token operator">::</span><span class="token function">toSimpleLicenceDTO</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * 新增{@link Licence}
     * @param request 请求体
     * @return
     */</span>
    <span class="token annotation punctuation">@Transactional</span><span class="token punctuation">(</span>rollbackFor <span class="token operator">=</span> <span class="token class-name">Throwable</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token class-name">LicenceAddRespData</span> <span class="token function">addLicence</span><span class="token punctuation">(</span><span class="token class-name">LicenceAddRequest</span> request<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">Licence</span> licence <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Licence</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        licence<span class="token punctuation">.</span><span class="token function">setOrganizationId</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span><span class="token function">getOrganizationId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        licence<span class="token punctuation">.</span><span class="token function">setLicenceType</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span><span class="token function">getLicenceType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        licence<span class="token punctuation">.</span><span class="token function">setProductName</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span><span class="token function">getProductName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        licence<span class="token punctuation">.</span><span class="token function">setLicenceMax</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span><span class="token function">getLicenceMax</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        licence<span class="token punctuation">.</span><span class="token function">setLicenceAllocated</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span><span class="token function">getLicenceAllocated</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        licence<span class="token punctuation">.</span><span class="token function">setComment</span><span class="token punctuation">(</span>request<span class="token punctuation">.</span><span class="token function">getComment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">save</span><span class="token punctuation">(</span>licence<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">LicenceAddRespData</span><span class="token punctuation">(</span>licence<span class="token punctuation">.</span><span class="token function">getLicenceId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * entity -&gt; simple dto
     * @param licence {@link Licence} entity
     * @return {@link SimpleLicenceDTO}
     */</span>
    <span class="token keyword">private</span> <span class="token class-name">SimpleLicenceDTO</span> <span class="token function">toSimpleLicenceDTO</span><span class="token punctuation">(</span><span class="token class-name">Licence</span> licence<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 省略</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * entity -&gt; dto
     * @param licence {@link Licence} entity
     * @param org {@link OrganizationDTO}
     * @return {@link LicenceDTO}
     */</span>
    <span class="token keyword">private</span> <span class="token class-name">LicenceDTO</span> <span class="token function">toLicenceDTO</span><span class="token punctuation">(</span><span class="token class-name">Licence</span> licence<span class="token punctuation">,</span> <span class="token class-name">OrganizationDTO</span> org<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token comment">// 省略</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * 校验{@link Licence}存在
     * @param licence
     */</span>
    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">checkNotNull</span><span class="token punctuation">(</span><span class="token class-name">Licence</span> licence<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token class-name">ResponseEnum</span><span class="token punctuation">.</span>LICENCE_NOT_FOUND<span class="token punctuation">.</span><span class="token function">assertNotNull</span><span class="token punctuation">(</span>licence<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

<span class="token punctuation">}</span>
</code></pre></div><blockquote><p>ps: 这里使用的DAO框架是<code>mybatis-plus</code>。
启动时，自动插入的数据为：</p></blockquote> <div class="language-mysql extra-class"><pre class="language-text"><code>-- licence
INSERT INTO licence (licence_id,  organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (1, 1, 'user','CustomerPro', 100,5);
INSERT INTO licence (licence_id,  organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (2, 1, 'user','suitability-plus', 200,189);
INSERT INTO licence (licence_id,  organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (3, 2, 'user','HR-PowerSuite', 100,4);
INSERT INTO licence (licence_id,  organization_id, licence_type, product_name, licence_max, licence_allocated)
VALUES (4, 2, 'core-prod','WildCat Application Gateway', 16,16);

-- organizations
INSERT INTO organization (id, name, contact_name, contact_email, contact_phone)
VALUES (1, 'customer-crm-co', 'Mark Balster', 'mark.balster@custcrmco.com', '823-555-1212');
INSERT INTO organization (id, name, contact_name, contact_email, contact_phone)
VALUES (2, 'HR-PowerSuite', 'Doug Drewry','doug.drewry@hr.com', '920-555-1212');
</code></pre></div><h5 id="开始验证"><a href="#开始验证" class="header-anchor">#</a> 开始验证</h5> <h6 id="捕获自定义异常"><a href="#捕获自定义异常" class="header-anchor">#</a> 捕获自定义异常</h6> <ol><li><p>获取不存在的 <code>licence</code> 详情：http://localhost:10000/licence/5。成功响应的请求：licenceId=1</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-b0fa0d8b8a5fb615.png" alt="校验非空"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-597cd570abdd1adf.png" alt="捕获 Licence not found 异常"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-66e274de724086f7.png" alt="Licence not found"></p></li> <li><p>根据不存在的 <code>licence type</code> 获取 <code>licence</code> 列表：http://localhost:10000/licence/list?licenceType=ddd。可选的 <code>licence type</code> 为：user、core-prod 。</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-921d0d8e9f127c9a.png" alt="校验非空"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-e5127756227feb6c.png" alt="捕获 Bad licence type 异常"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-8eb5bb9e80cfcb2a.png" alt="Bad licence type"></p></li></ol> <h6 id="捕获进入-controller-前的异常"><a href="#捕获进入-controller-前的异常" class="header-anchor">#</a> 捕获进入 Controller 前的异常</h6> <ol><li><p>访问不存在的接口：http://localhost:10000/licence/list/ddd</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-046f7daacb9b7370.png" alt="捕获404异常"></p></li> <li><p>http 方法不支持：http://localhost:10000/licence</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-1770a461fdadbe15.png" alt="PostMapping"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-6da1112b9139f355.png" alt="捕获 Request method not supported 异常"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-bd4def36d3ab7bf8.png" alt="Request method not supported"></p></li> <li><p>校验异常1：http://localhost:10000/licence/list?licenceType=</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-407ace67379ee00f.png" alt="getLicences"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-3622c3b17807d644.png" alt="LicenceParam"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-fe8c5cccd8b2ab68.png" alt="捕获参数绑定校验异常"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-ab9e298336aef255.png" alt="licence type cannot be empty"></p></li> <li><p>校验异常2：post 请求，这里使用postman模拟。</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-7861c07ecd7fed16.png" alt="addLicence"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-41198ccf27557fa6.png" alt="LicenceAddRequest"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-cd05247313b434e4.png" alt="请求url即结果"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-dd5a6c11087e8c3f.png" alt="捕获参数绑定校验异常"></p></li></ol> <p>注：因为参数绑定校验异常的异常信息的获取方式与其它异常不一样，所以才把这2种情况的异常从 <strong>进入 Controller 前的异常</strong> 单独拆出来，下面是异常信息的收集逻辑：</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-f0f676306b2e7bdb.png" alt="异常信息的收集"></p> <h6 id="捕获未知异常"><a href="#捕获未知异常" class="header-anchor">#</a> 捕获未知异常</h6> <p>假设我们现在随便对 <code>Licence</code> 新增一个字段 <code>test</code>，但不修改数据库表结构，然后访问：http://localhost:10000/licence/1。</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-a6a6033aa2a52dda.png" alt="增加test字段"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-0cf67f1abd300137.png" alt="捕获数据库异常"></p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-0be9587ad3303c22.png" alt="Error querying database"></p> <h4 id="小结"><a href="#小结" class="header-anchor">#</a> 小结</h4> <p>可以看到，测试的异常都能够被捕获，然后以 <code>code</code>、<code>message</code> 的形式返回。每一个项目/模块，在定义业务异常的时候，只需定义一个枚举类，然后实现接口 <code>BusinessExceptionAssert</code>，最后为每一种业务异常定义对应的枚举实例即可，而不用定义许多异常类。使用的时候也很方便，用法类似断言。</p> <h3 id="扩展"><a href="#扩展" class="header-anchor">#</a> 扩展</h3> <p>在生产环境，若捕获到 <strong>未知异常</strong> 或者 <code>ServletException</code>，因为都是一长串的异常信息，若直接展示给用户看，显得不够专业，于是，我们可以这样做：当检测到当前环境是生产环境，那么直接返回 &quot;网络异常&quot;。</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-47f35586cd908fb1.png" alt="生产环境返回“网络异常”"></p> <p>可以通过以下方式修改当前环境：</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-3873860b9340dc4b.png" alt="修改当前环境为生产环境"></p> <h2 id="总结"><a href="#总结" class="header-anchor">#</a> 总结</h2> <p>使用 <strong>断言</strong> 和 <strong>枚举类</strong> 相结合的方式，再配合统一异常处理，基本大部分的异常都能够被捕获。为什么说大部分异常，因为当引入 <code>spring cloud security</code> 后，还会有认证/授权异常，网关的服务降级异常、跨模块调用异常、远程调用第三方服务异常等，这些异常的捕获方式与本文介绍的不太一样，不过限于篇幅，这里不做详细说明，以后会有单独的文章介绍。</p> <p>另外，当需要考虑国际化的时候，捕获异常后的异常信息一般不能直接返回，需要转换成对应的语言，不过本文已考虑到了这个，获取消息的时候已经做了国际化映射，逻辑如下：</p> <p><img src="https://upload-images.jianshu.io/upload_images/6082182-f80c55ebc282b7b9.png" alt="获取国际化消息"></p> <p>最后总结，全局异常属于老生长谈的话题，希望这次通过手机的项目对大家有点指导性的学习。大家根据实际情况自行修改。</p> <p>也可以采用以下的jsonResult对象的方式进行处理，也贴出来代码.</p> <div class="language-java extra-class"><pre class="language-java"><code><span class="token annotation punctuation">@Slf4j</span>
<span class="token annotation punctuation">@RestControllerAdvice</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">GlobalExceptionHandler</span> <span class="token punctuation">{</span>

    <span class="token comment">/**
     * 没有登录
     * @param request
     * @param response
     * @param e
     * @return
     */</span>
    <span class="token annotation punctuation">@ExceptionHandler</span><span class="token punctuation">(</span><span class="token class-name">NoLoginException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">noLoginExceptionHandler</span><span class="token punctuation">(</span><span class="token class-name">HttpServletRequest</span> request<span class="token punctuation">,</span><span class="token class-name">HttpServletResponse</span> response<span class="token punctuation">,</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">&quot;[GlobalExceptionHandler][noLoginExceptionHandler] exception&quot;</span><span class="token punctuation">,</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">JsonResult</span> jsonResult <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JsonResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        jsonResult<span class="token punctuation">.</span><span class="token function">setCode</span><span class="token punctuation">(</span><span class="token class-name">JsonResultCode</span><span class="token punctuation">.</span>NO_LOGIN<span class="token punctuation">)</span><span class="token punctuation">;</span>
        jsonResult<span class="token punctuation">.</span><span class="token function">setMessage</span><span class="token punctuation">(</span><span class="token string">&quot;用户登录失效或者登录超时,请先登录&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> jsonResult<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     *  业务异常
     * @param request
     * @param response
     * @param e
     * @return
     */</span>
    <span class="token annotation punctuation">@ExceptionHandler</span><span class="token punctuation">(</span><span class="token class-name">ServiceException</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">businessExceptionHandler</span><span class="token punctuation">(</span><span class="token class-name">HttpServletRequest</span> request<span class="token punctuation">,</span><span class="token class-name">HttpServletResponse</span> response<span class="token punctuation">,</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">&quot;[GlobalExceptionHandler][businessExceptionHandler] exception&quot;</span><span class="token punctuation">,</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">JsonResult</span> jsonResult <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JsonResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        jsonResult<span class="token punctuation">.</span><span class="token function">setCode</span><span class="token punctuation">(</span><span class="token class-name">JsonResultCode</span><span class="token punctuation">.</span>FAILURE<span class="token punctuation">)</span><span class="token punctuation">;</span>
        jsonResult<span class="token punctuation">.</span><span class="token function">setMessage</span><span class="token punctuation">(</span><span class="token string">&quot;业务异常,请联系管理员&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> jsonResult<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">/**
     * 全局异常处理
     * @param request
     * @param response
     * @param e
     * @return
     */</span>
    <span class="token annotation punctuation">@ExceptionHandler</span><span class="token punctuation">(</span><span class="token class-name">Exception</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span>
    <span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">exceptionHandler</span><span class="token punctuation">(</span><span class="token class-name">HttpServletRequest</span> request<span class="token punctuation">,</span><span class="token class-name">HttpServletResponse</span> response<span class="token punctuation">,</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token string">&quot;[GlobalExceptionHandler][exceptionHandler] exception&quot;</span><span class="token punctuation">,</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token class-name">JsonResult</span> jsonResult <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JsonResult</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        jsonResult<span class="token punctuation">.</span><span class="token function">setCode</span><span class="token punctuation">(</span><span class="token class-name">JsonResultCode</span><span class="token punctuation">.</span>FAILURE<span class="token punctuation">)</span><span class="token punctuation">;</span>
        jsonResult<span class="token punctuation">.</span><span class="token function">setMessage</span><span class="token punctuation">(</span><span class="token string">&quot;系统错误,请联系管理员&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> jsonResult<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div></div> <footer class="page-edit" style="display:none;"><!----> <!----></footer> <!----> <!----> <!----></main> <!----></div></div></div></div><div class="global-ui"><div class="back-to-ceiling" style="right:1rem;bottom:6rem;width:2.5rem;height:2.5rem;border-radius:.25rem;line-height:2.5rem;display:none;" data-v-db14854a data-v-db14854a><svg t="1574745035067" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5404" class="icon" data-v-db14854a><path d="M526.60727968 10.90185116a27.675 27.675 0 0 0-29.21455937 0c-131.36607665 82.28402758-218.69155461 228.01873535-218.69155402 394.07834331a462.20625001 462.20625001 0 0 0 5.36959153 69.94390903c1.00431239 6.55289093-0.34802892 13.13561351-3.76865779 18.80351572-32.63518765 54.11355614-51.75690182 118.55860487-51.7569018 187.94566865a371.06718723 371.06718723 0 0 0 11.50484808 91.98906777c6.53300375 25.50556257 41.68394495 28.14064038 52.69160883 4.22606766 17.37162448-37.73630017 42.14135425-72.50938081 72.80769204-103.21549295 2.18761121 3.04276886 4.15646224 6.24463696 6.40373557 9.22774369a1871.4375 1871.4375 0 0 0 140.04691725 5.34970492 1866.36093723 1866.36093723 0 0 0 140.04691723-5.34970492c2.24727335-2.98310674 4.21612437-6.18497483 6.3937923-9.2178004 30.66633723 30.70611158 55.4360664 65.4791928 72.80769147 103.21549355 11.00766384 23.91457269 46.15860503 21.27949489 52.69160879-4.22606768a371.15156223 371.15156223 0 0 0 11.514792-91.99901164c0-69.36717486-19.13165746-133.82216804-51.75690182-187.92578088-3.42062944-5.66790279-4.76302748-12.26056868-3.76865837-18.80351632a462.20625001 462.20625001 0 0 0 5.36959269-69.943909c-0.00994388-166.08943902-87.32547796-311.81420293-218.6915546-394.09823051zM605.93803103 357.87693858a93.93749974 93.93749974 0 1 1-187.89594924 6.1e-7 93.93749974 93.93749974 0 0 1 187.89594924-6.1e-7z" p-id="5405" data-v-db14854a></path><path d="M429.50777625 765.63860547C429.50777625 803.39355007 466.44236686 1000.39046097 512.00932183 1000.39046097c45.56695499 0 82.4922232-197.00623328 82.5015456-234.7518555 0-37.75494459-36.9345906-68.35043303-82.4922232-68.34111062-45.57627738-0.00932239-82.52019037 30.59548842-82.51086798 68.34111062z" p-id="5406" data-v-db14854a></path></svg></div><!----></div></div>
    <script src="/assets/js/app.447d4224.js" defer></script><script src="/assets/js/3.9d76740c.js" defer></script><script src="/assets/js/1.c4fd7d2e.js" defer></script><script src="/assets/js/173.96a3afee.js" defer></script>
  </body>
</html>
